/*
 * Unidata Platform
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *
 * Commercial License
 * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *
 * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 * For clarification or additional options, please contact: info@unidata-platform.com
 * -------
 * Disclaimer:
 * -------
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 */
package org.unidata.mdm.rest.meta.service;

import static org.unidata.mdm.meta.type.rendering.MetaModelInputRenderingAction.APPLY_META_MODEL_GRAPH;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.cxf.jaxrs.ext.multipart.Multipart;
import org.springframework.beans.factory.annotation.Autowired;
import org.unidata.mdm.core.service.AsyncExecutor;
import org.unidata.mdm.core.service.SecurityService;
import org.unidata.mdm.core.util.SecurityUtils;
import org.unidata.mdm.meta.constant.IEConstants;
import org.unidata.mdm.meta.context.ApplyUploadedModelRequestContext;
import org.unidata.mdm.meta.context.ExportContext;
import org.unidata.mdm.meta.context.ExportModelRequestContext;
import org.unidata.mdm.meta.context.UploadModelRequestContext;
import org.unidata.mdm.meta.type.input.meta.MetaGraph;
import org.unidata.mdm.rest.meta.converter.graph.MetaGraphDTOToROConverter;
import org.unidata.mdm.rest.meta.exception.MetaRestExceptionIds;
import org.unidata.mdm.rest.meta.ro.MetaGraphRO;
import org.unidata.mdm.rest.meta.ro.MetaGraphROToDTOConverter;
import org.unidata.mdm.rest.meta.ro.export.ExportParamsRO;
import org.unidata.mdm.rest.meta.type.rendering.MetaModelInputRenderingAction;
import org.unidata.mdm.rest.system.ro.RestResponse;
import org.unidata.mdm.rest.system.ro.UpdateResponse;
import org.unidata.mdm.rest.system.service.AbstractRestService;
import org.unidata.mdm.system.exception.PlatformBusinessException;
import org.unidata.mdm.system.service.ExecutionService;
import org.unidata.mdm.system.service.RenderingService;
import org.unidata.mdm.system.type.rendering.VoidInputSource;

/**
 * @author maria.chistyakova
 * @since 20.04.2020
 */
@Path(MetaInputOutputRestService.SERVICE_PATH)
@Consumes({"application/json"})
@Produces({"application/json"})
public class MetaInputOutputRestService extends AbstractRestService {

    @Autowired
    private ExecutionService executionService;

    @Autowired
    private RenderingService renderingService;
    /**
     * HZ notifyer.
     */
//    @Autowired
//    private MatchingRulesChangesNotifier matchingRulesChangesNotifier;
    /**
     * Async executor.
     */
    @Autowired
    private AsyncExecutor asyncExecutor;
    /**
     *
     */
    @Autowired
    private SecurityService securityService;
    /**
     * Service path.
     */
    public static final String SERVICE_PATH = "model-ie";

    /**
     * Upload.
     *
     * @param modelFile the model file
     * @param isOverride is override or not
     * @return the response
     * @throws Exception the exception
     */
    @POST
    @Path("/upload")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response upload(@Multipart(value = "modelFile") Attachment modelFile,
                           @Multipart(value = "override") boolean isOverride) throws IOException {
        java.nio.file.Path toImport = saveFileToTempFolder(modelFile);

        UploadModelRequestContext context = UploadModelRequestContext.builder()
                .setZipPath(toImport)
                .setOverride(isOverride)
                .build();

        // no connectors - no rendering

        MetaGraph result = executionService.execute(context);

        // do not rendering, indepedment
        MetaGraphRO convert = MetaGraphDTOToROConverter.convert(result);

        return ok(new RestResponse<>(convert));
    }

    /**
     * Apply.
     *
     * @param metaGraphRO the meta graph
     * @return the response
     */
    @POST
    @Path("/apply")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response apply(MetaGraphRO metaGraphRO) {
        MetaGraph metaGraphInput = MetaGraphROToDTOConverter.convert(metaGraphRO);
        ApplyUploadedModelRequestContext.Builder builder =
                ApplyUploadedModelRequestContext.builder().setMetaGraph(metaGraphInput);

        renderingService.renderInput(APPLY_META_MODEL_GRAPH, builder, metaGraphInput);

        executionService.execute(builder.build());

        return ok(new RestResponse<>());
    }

    /**
     * Export meta model to zip file.
     *
     * @param exportParamsRO the export parameters
     * @return the response
     */
    @POST
    @Path("/export")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response export(final ExportParamsRO exportParamsRO) {

        final String storageId = "default";

        String token = SecurityUtils.getCurrentUserToken();
        asyncExecutor.async(() -> {
            securityService.authenticate(token, true);

            java.nio.file.Path path = Paths.get(
                    IEConstants.EXPORT_PATH.toString(),
                    storageId +
                            DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd_HH-mm-ss")
            );
            ExportContext exportContext = new ExportContext(exportParamsRO.isRoles(), exportParamsRO.isUsers());

            ExportModelRequestContext.Builder builder
                    = new ExportModelRequestContext.Builder()
                    .storageId(storageId)
                    .path(path)
                    .exportContext(exportContext);

            renderingService.renderInput(MetaModelInputRenderingAction.EXPORT_META_MODEL_DRAFT, builder, VoidInputSource.INSTANCE);

            executionService.execute(builder.build());
        });
        return ok(new UpdateResponse(true, storageId));
    }

    /**
     * Save file to temp folder.
     *
     * @param attachment the attachment
     * @return the java.nio.file. path
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static java.nio.file.Path saveFileToTempFolder(Attachment attachment) throws IOException {
        String fileName = attachment.getContentDisposition().getParameter("filename");
        if (StringUtils.isEmpty(fileName) || !StringUtils.endsWithIgnoreCase(fileName, ".zip")) {
            throw new PlatformBusinessException("File format not supported. Supported only zip files.",
                    MetaRestExceptionIds.EX_META_IMPORT_MODEL_INVALID_FILE_FORMAT);
        }

        String pathToModelLocation = System.getProperty("catalina.base")
                + File.separator
                + "temp"
                + File.separator
                + "to_import"
                + File.separator
                + "model";

        Files.createDirectories(Paths.get(pathToModelLocation));

        java.nio.file.Path path = Paths.get(
                pathToModelLocation
                        + File.separator
                        + fileName
        );
        Files.deleteIfExists(path);
        InputStream in = attachment.getObject(InputStream.class);
        Files.copy(in, path);
        return path;
    }
}
